﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX.DirectInput;
//using System.Windows.Forms;
using System.Globalization;

namespace MercuryFE
{
    public enum InputDeviceType
    {
        keyboard, mouse, joystick, notsupported
    };

    public enum InputDeviceTypeAction
    {
        Up, Down, Left, Right, Select, Back, Info
    };


    public class InputDevice : IDisposable
    {
        InputDeviceType _deviceType = InputDeviceType.notsupported; // set to keyboard by default
        CooperativeLevel _cooperativeLevel = CooperativeLevel.Nonexclusive | CooperativeLevel.Foreground | CooperativeLevel.NoWinKey;
        //Control _formControl = null;
        Guid _identifier;
        string _productName;
        Device _device;
        IntPtr _handle = IntPtr.Zero;
        int _mouseSensitivity = 10;

        public InputDeviceType DeviceType { get { return _deviceType; } }

        Dictionary<string, InputDeviceTypeAction> _mappings = new Dictionary<string, InputDeviceTypeAction>();

        public Dictionary<string, InputDeviceTypeAction> Mappings { set { _mappings = value; } }

        //Dictionary<string, DeviceTypeAction> _keyMappings = new Dictionary<string, DeviceTypeAction> 
        //{     
        //      { "UpArrow", DeviceTypeAction.Up }
        //    , { "DownArrow", DeviceTypeAction.Down }
        //    , { "LeftArrow", DeviceTypeAction.Left  }
        //    , { "RightArrow", DeviceTypeAction.Right } 
        //    , { "D1", DeviceTypeAction.Select }
        //    , { "D2",DeviceTypeAction.Back }
        //    , { "P", DeviceTypeAction.Pause }
        //};

        //Dictionary<int, DeviceTypeAction> _mouseButtonMappings = new Dictionary<int, DeviceTypeAction>
        //{
        //      {0, DeviceTypeAction.Select }
        //    , {1, DeviceTypeAction.Back }
        //    , {2, DeviceTypeAction.Pause }
        //};



        //int _offset;



        public InputDevice(IntPtr winHandle, InputDeviceType deviceType, Guid identifier, string productName)
        {
            _deviceType = deviceType;
            _identifier = identifier;
            _productName = productName;
            _handle = winHandle;
        }


        public void CreateDevice(DirectInput dinput)
        {
            switch (_deviceType)
            {
                case InputDeviceType.keyboard :
                    KeyboardCreateDevice(dinput);
                    break;
                case InputDeviceType.joystick :
                    JoystickCreateDevice(dinput);
                    break;
                case InputDeviceType.mouse:
                    MouseCreateDevice(dinput);
                    break;

            }


        }

        public InputDeviceState ReadData(bool useRaw)
        {
            InputDeviceState state = new InputDeviceState();
            switch (_deviceType)
            {
                case InputDeviceType.keyboard:
                    state = KeyboardReadData(useRaw);
                    break;
                case InputDeviceType.joystick:
                    state = JoystickReadData(useRaw);
                    break;
                case InputDeviceType.mouse:
                    state = MouseReadData(useRaw);
                    break;

            }
            return state;
        }


        public void UnacquireDevice()
        {
            if (_device != null)
            {
                _device.Unacquire();
                //_device.Dispose();
                //_device = null;
            }

        }

        //public void ReleaseDevice()
        //{
        //    if (_device != null)
        //    {
        //        _device.Unacquire();
        //        //_device.Dispose();
        //        //_device = null;
        //    }

        //}


        public void Dispose()
        {
            UnacquireDevice();
            _device = null;
        }

        #region keyboard

        private void KeyboardCreateDevice(DirectInput dinput)
        {

            // create the device
            try
            {
                _device = new Keyboard(dinput);
                ((Keyboard)_device).SetCooperativeLevel(_handle, _cooperativeLevel);
        
            }
            catch (DirectInputException e)
            {
                //MessageBox.Show(e.Message);
                //return;
            }

            // don't create buffer - since we only want to read the current state
             ((Keyboard)_device).Properties.BufferSize = 8;


            // acquire the device
            ((Keyboard)_device).Acquire();

        }

        private InputDeviceState KeyboardReadData(bool useRaw)
        {
            if (((Keyboard)_device).Acquire().IsFailure)
                return null;

            if (((Keyboard)_device).Poll().IsFailure)
                return null;

           
            //KeyboardState packet = ((Keyboard)_device).GetCurrentState();


            IList<KeyboardState> bufferedData =  ((Keyboard)_device).GetBufferedData();

            if (SlimDX.Result.Last.IsFailure)
                return null;

            // fix this up to handle key mappings, and return action accordingly

            InputDeviceState ids = new InputDeviceState();

           // StringBuilder data = new StringBuilder();
            if (!useRaw)
            {
                bool KeyPressFound = false;
                foreach (KeyboardState kbs in bufferedData)
                {
                    foreach (Key key in kbs.PressedKeys)
                    {
                        if (_mappings.ContainsKey(key.ToString()))
                        {
                            string action = _mappings[key.ToString()].ToString();
                            ids.SetValue(action);
                            KeyPressFound = true;
                            break;  // after first matching key press, break...
                        }
                    }
                    if (KeyPressFound)
                        break;
                }

               
                //foreach (Key key in packet.PressedKeys)
                //{
                //    if (_mappings.ContainsKey(key.ToString()))
                //    {
                //        string action = _mappings[key.ToString()].ToString();
                //        ids.SetValue(action);
                //        break;  // after first matching key press, break...
                //    }
                //}
            }
            else
            {
                bool KeyPressFound = false;
                foreach (KeyboardState kbs in bufferedData)
                {
                    foreach (Key key in kbs.PressedKeys)
                    {
                        if (kbs.PressedKeys.Count > 0)
                        {
                            ids.RawValue = kbs.PressedKeys[0].ToString();
                            KeyPressFound = true;
                            break; 

                       }
                    }
                    if (KeyPressFound)
                        break;
                }



                //if(packet.PressedKeys.Count > 0) {
                //    ids.RawValue = packet.PressedKeys[0].ToString();
                //}
            }

           
            
           // ids.Value = data.ToString();
            return ids;

        }

        #endregion


        #region joystick
        private void JoystickCreateDevice(DirectInput dinput)
        {

            // create the device
            try
            {
                _device = new Joystick(dinput, _identifier);
                ((Joystick)_device).SetCooperativeLevel(_handle, CooperativeLevel.Exclusive | CooperativeLevel.Foreground);
            }
            catch (DirectInputException)
            {
                return;
            }

            if (_device == null)
            {
               // MessageBox.Show("There are no joysticks attached to the system.");
                return;
            }
            ((Joystick)_device).Properties.BufferSize = 8;

            // acquire the device
            ((Joystick)_device).Acquire();


        }

        private InputDeviceState JoystickReadData(bool useRaw)
        {
            if (((Joystick)_device).Acquire().IsFailure)
                return null;

            if (((Joystick)_device).Poll().IsFailure)
                return null;

            JoystickState state = ((Joystick)_device).GetCurrentState();

            IList<JoystickState> bufferedData =  ((Joystick)_device).GetBufferedData();
            if (SlimDX.Result.Last.IsFailure || bufferedData.Count ==0)
                return null;

            // temp for testing
            string strText = null;
            StringBuilder sb = new StringBuilder();

            sb.Append(" x=" + state.X.ToString(CultureInfo.CurrentCulture));
            sb.Append(" y=" + state.Y.ToString(CultureInfo.CurrentCulture));
            sb.Append(" z=" + state.Z.ToString(CultureInfo.CurrentCulture));

            sb.Append(" rotx=" + state.RotationX.ToString(CultureInfo.CurrentCulture));
            sb.Append(" roty=" + state.RotationY.ToString(CultureInfo.CurrentCulture));
            sb.Append(" rotz=" + state.RotationZ.ToString(CultureInfo.CurrentCulture));

            int[] slider = state.GetSliders();

            sb.Append(" sli1=" + slider[0].ToString(CultureInfo.CurrentCulture));
            sb.Append(" sli2=" + slider[1].ToString(CultureInfo.CurrentCulture));

            int[] pov = state.GetPointOfViewControllers();

            sb.Append(" pov0=" + pov[0].ToString(CultureInfo.CurrentCulture));
            sb.Append(" pov1=" + pov[1].ToString(CultureInfo.CurrentCulture));
            sb.Append(" pov2=" + pov[2].ToString(CultureInfo.CurrentCulture));
            sb.Append(" pov3=" + pov[3].ToString(CultureInfo.CurrentCulture));

            bool[] buttons = state.GetButtons();

            for (int b = 0; b < buttons.Length; b++)
            {
                if (buttons[b])
                    strText += b.ToString("00 ", CultureInfo.CurrentCulture);
            }
            sb.Append(" buttons=" + strText);

            //return sb.ToString();
            InputDeviceState ids = new InputDeviceState();
            return ids;
        }

        #endregion


        #region mouse
        private void MouseCreateDevice(DirectInput dinput)
        {

            // create the device
            try
            {
                _device = new Mouse(dinput);
                ((Mouse)_device).SetCooperativeLevel(_handle, _cooperativeLevel);
            }
            catch (DirectInputException e)
            {
                //MessageBox.Show(e.Message);
                //return;
            }
            ((Mouse)_device).Properties.BufferSize = 8;

            // acquire the device
            ((Mouse)_device).Acquire();

        }

        private InputDeviceState MouseReadData(bool useRaw)
        {
            InputDeviceState ids = new InputDeviceState();

            if (((Mouse)_device).Acquire().IsFailure)
                return null;

            if (((Mouse)_device).Poll().IsFailure)
                return null;


            IList<MouseState> bufferedData = ((Mouse)_device).GetBufferedData();
            if (SlimDX.Result.Last.IsFailure || bufferedData.Count == 0)
                return null;

            if (!useRaw)
            {

                // get the 'culmination' of all the movements...
                MouseState result = new MouseState();
                foreach (MouseState packet in bufferedData)
                {
                    result.X += packet.X;
                    result.Y += packet.Y;
                    result.Z += packet.Z;
                }

                ids.Up = ((result.Y < -_mouseSensitivity) || result.Z > 0 ? true : false);
                ids.Down = ((result.Y > _mouseSensitivity) || result.Z < 0 ? true : false);

                ids.Left = (result.X < -_mouseSensitivity ? true : false);
                ids.Right = (result.X > _mouseSensitivity ? true : false);

                // foreach button, check if it has been pressed at all... then set true and break
                for (int i = 0; i < 8; i++)
                {
                    foreach (MouseState packet in bufferedData)
                    {
                        if (packet.IsPressed(i))
                        {
                            ids.Buttons[i] = true;

                            if (_mappings.ContainsKey(i.ToString()))
                            {
                                string action = _mappings[i.ToString()].ToString();
                                ids.SetValue(action);

                            }

                            break;
                        }
                    }
                }
            }
            else
            {
                // for raw - only handle buttons for the moment
                bool mouseButtonPressed = false;

                for (int i = 0; i < 8; i++)
                {
                    foreach (MouseState packet in bufferedData)
                    {
                        if (packet.IsPressed(i))
                        {
                            mouseButtonPressed = true;
                            ids.RawValue = i.ToString();
                            break;
                        }
                    }
                    if (mouseButtonPressed)
                        break;
                }
            }


            return ids;
        }

        #endregion
    }
}
